This file contains last-minute fixes and information that couldn't be added to the book.
I will maintain an up-to-date version of this page on my VB-2-The-Max web site, so please check it periodically at www.vb2themax.com/Books/ProgrammingVBNET/Updates.asp.
Francesco Balena
fbalena@vb2themax.com
You might have noticed that all the samples with division by zero exceptions are performed on integer values. Here's the reason: in Visual Basic .NET, dividing a floating point number by zero doesn't throw an exception; instead, it returns an infinite value, that appears as Infinity or –Infinity (depending on the sign of the first operand of the division). In other words, infinity is a valid value for IEEE floating point numbers, and in fact the Single and Double classes exposes the PositiveInfinity and NegativeInfinity constants, and the IsInfinity, IsPositiveInfinity, and IsNegativeInfinity methods. Infinite values can appear in operations, so for example you can add to infinite values with the same sign (which returns an infinite value with the same sign) and you can divide any number by an infinite value (in which case you get zero). If you attempt to sum two infinite values of different sign you get NaN (not a number).
In the section "Trapping events with AddHandler" I mention that you don't have to call the RemoveHandler method to unregister all the events an object has registered for, because the runtime does the cleanup automatically when the object is set to Nothing. This is correct, but only if the other object – that is, the object that is the source of the event – is also set to Nothing.
To make things clear, let’s call SOURCE the object that is the source of the event, and HANDLER the object that handles the event. The problem in not explicitly using RemoveHandler is that SOURCE has a hidden reference to HANDLER (because events are actually handled through delegates, so HANDLER is the Target property of the underlying delegate), therefore HANDLER can't be finalized until SOURCE is also finalized. Even worse, the event handler in HANDLER might execute even after the HANDLER object has been "logically destroyed" by the main application. The moral of the story is simple: always call RemoveHandler in the Dispose method of an object that has created an event handler with the AddHandler command.
In the note at the end of the "Override Variations" section, I mention that nonoverridable methods are only marginally faster (15 percent) than overridable methods. In the release version of Visual Basic .NET, however, I've found that they are now remarkably faster, up to twice as fast. Therefore my advice is to attempt to avoid overridable methods in a time critical loop. Notice that you see this performance improvement only if you compile without debug support (Ctrl+F5) and run the application from outside Visual Studio.
By the way, this consideration also applies to methods in the main class interface and methods in a secondary interface (that is, a method that is followed by the Inherits keyword). It turns out that methods in the class interface are remarkably faster than interface methods – or even the same method if called through an interface variable – so you should always call a method though a class variable if you have a choice.
In the "Redefining Events" section I explain that a derived class can trap events defined in the base class by defining a WithEvents variables that points to Me. Actually the solution is much simpler: You can use the MyBase keyword in the Handles clause, so you can directly trap events from your base class. For example, all the Windows Forms applications I show in Chapter 16 use this technique, and I can hardly believe I overlooked this mistake (which dates back to when VB.NET was in Beta 1 version) in all my revisions of the manuscript. The example based on the FileDataReader class should be rewritten as follows:
Class FileDataReader
Inherits DataReader
' This counter must be incremented after each event.
Public EventCounter As Integer
Private Sub NotifyDataAvailable() Handles MyBase.DataAvailable
' Increment the counter.
EventCounter += 1
End Sub
End Class
In Visual Studio .NET you can simply select the (Base Class Events) element in the leftmost combo box and then the event name in the rightmost combo box.
In the section "Boxing and Unboxing" I comment that a benchmark that shows that boxing an argument passed to an empty procedure can slow down your code by a factor of 6. In the release version of Visual Basic .NET, the code that does the boxing/unboxing takes about 2 seconds, while the one that doesn't takes about 0.06 second, so its about 32 times faster. Also in this case, you see the improvement only if you compile without debug support.
In the "Shared Methods" section related to strings, I mention that the String.CompareOrdinal method is three to four times faster than Compare, because it works with the numeric code of individual characters. In the release version of Visual Basic .NET I've found it to be even 6-7 times faster than Compare.
In the "Array Class" section I mention that you can check whether an array is initialized or not by testing it for Nothing. Further explorations showed that Visual Basic .NET even supports arrays that are initialized but contains zero elements. You can create such an array as follows
Dim arr(-1) As Integer
and you can check how many elements it contains by using the Length property, as you would do as usual. Such a zero-length array might be useful in some special cases, and would avoid the test for Nothing that you would have to do in regular circumstances.
In "The Mutex Class" section I omit to explain one of the most interesting (and useful) features of mutexes. Unlike most other synchronization objects, mutexes can be assigned a name and mutexes with the same name are shared among different processes. You can create an instance of a Mutex with a name using this syntax:
Dim m As New Mutex(False, "mutexname")
If a Mutex with that name already exist in the system, the caller gets a reference to it, otherwise a new Windows Mutex object is created. This mechanism permits you to share Mutex objects among different applications, which can therefore synchronize access to shared resources.
In the section "The EnableViewState property," I mention that you have to explicitly set the EnableViewState property to False for those controls whose value shouldn't be persisted between postbacks. Notice that, however, regardless of the value of the EnableViewState property, ASP.NET always restores the state of a few controls – such as TextBox and CheckBox controls – because their values are included in the form that is posted back to the server. To clear these controls you must explicitly assign a value to them before the page is sent back to the client.